package zorm

import (
	"errors"
	"fmt"
	"strconv"
	"strings"
	"time"
)

const (
	DOMAIN_FIELD  = 1 //真假类型
	DOMAIN_NUM    = 2 //数字类型
	DOMAIN_DEFINE = 3 //字段
	DOMAIN_BOOL   = 4 //真假值
	DOMAIN_STRING = 5 //字符串
	DOMAIN_ARRAY  = 6 //数据组
)

type DOMAIN struct {
	Zdb       *ZDB
	Model     string                 //当前的模块名称
	allstring string                 //全部字符串
	Domain    []rune                 //由于汉字问题，换成rune
	Sql       string                 //需要返回的字符串
	Context   map[string]interface{} //其它信息
	tp        string                 //类型
	uid       *SUser                 //用户
}

//获取一个模块的字段
func (c *ZDB) GetModelDomain(uid *SUser, strptr interface{}, field string) string {
	sqls := ""
	if c.isDebug == true {
		st := time.Now()
		defer func() {
			fmt.Printf("ORM %08s %f\t %s\r\n", time.Now().Format("15:04:05"), time.Now().Sub(st).Seconds(), sqls)
		}()
	}
	if uid == nil { //内置没有domain
		return ""
	}
	if uid.Id == 0 { //管理员没有domain
		return ""
	}
	minfo, err := GetTableInfo(strptr)
	if err != nil {
		panic(err)
	}
	sqls = "SELECT domain FROM s_rule WHERE model='" + minfo.Name + "' AND " + field + "=true"
	rows, err := c.Db.Query(sqls)
	if err != nil {
		panic(err.Error() + "详细信息:" + sqls)
	}
	defer rows.Close()
	Domain := ""
	for rows.Next() {
		dms := ""
		if err := rows.Scan(&dms); err != nil {
			panic(err)
		}
		if len(Domain) == 0 {
			if len(dms) != 0 {
				Domain = dms
			}
		} else {
			if len(dms) != 0 {
				Domain = "(" + Domain + ")|(" + dms + ")"
			}
		}
	}
	return Domain
}
func (c *DOMAIN) Parse() error {
	if len(c.Model) == 0 {
		panic("asdf")
	}
	if len(c.Domain) == 0 { //如果没有需要解析的，直接返回
		return nil
	}
	if err := c.ParseOne(); err != nil {
		return err
	}
	for i := 0; i < 1000; i++ {
		if len(c.Domain) == 0 {
			return nil
		}
		by := c.Domain[0]
		if by == '|' || by == '&' {
			c.Domain = c.Domain[1:]
			nc := &DOMAIN{Domain: c.Domain, Context: c.Context, Model: c.Model, uid: c.uid, Zdb: c.Zdb}
			if err := nc.ParseOne(); err != nil {
				return err
			}
			if by == '|' {
				c.Sql = "(" + c.Sql + ") OR (" + nc.Sql + ")"
			} else {
				c.Sql = "(" + c.Sql + ") AND (" + nc.Sql + ")"
			}
			c.Domain = nc.Domain //剩余数据拷贝
		} else {
			return errors.New("两个Domain之间必须为:| &,当前:" + string(c.Domain))
		}
	}
	panic(fmt.Sprintf("1000圈都没有处理完:%v", c))
}
func (c *DOMAIN) ParseOne() error {
	if len(c.Domain) == 0 { //如果没有条件，则返回空
		return nil //直接返回
	}
	if c.Domain[0] != '[' {
		return errors.New("起始必须是'[',字符串:" + string(c.Domain))
	}
	c.Domain = c.Domain[1:]

	srcword, srcwordtype, err := c.GetOneWord()
	if err != nil {
		return errors.New("字段不正确:" + err.Error())
	}
	if c.Domain[0] != ',' {
		panic("项目之间分隔符必须为',', 当前后面是:" + string(c.Domain))
	} else {
		c.Domain = c.Domain[1:]
	}
	control := ""
	for {
		if len(c.Domain) == 0 {
			return errors.New("操作符后面必须为',':")
		}
		by := c.Domain[0]
		if by == ',' {
			c.Domain = c.Domain[1:] //跳过逗号
			break
		}
		control += string(by)
		c.Domain = c.Domain[1:]
	}
	if len(control) == 0 {
		return errors.New("操作符不能为空")
	}
	decword, decwordtype, err := c.GetOneWord()
	if err != nil {
		return errors.New("目标不正确:" + err.Error())
	}
	//if (srcwordtype == DOMAIN_FIELD) || (srcwordtype == DOMAIN_DEFINE) {
	if err := c.JX_Interface(srcword, srcwordtype, control, decword, decwordtype); err != nil {
		return err
	}
	//}
	if c.Domain[0] != ']' {
		return errors.New("条件最后必须为]:" + string(c.Domain))
	}
	c.Domain = c.Domain[1:]
	return nil
}

//功能 获取一个数组字符串，比如 (1,2,3)
func (c *DOMAIN) GetNumberArr() ([]int, error) {
	if c.Domain[0] != '(' {
		return nil, errors.New("第一个字符必须是(" + string(c.Domain))
	}
	c.Domain = c.Domain[1:] //去掉(
	var ret []int
	for {
		if n, err := c.GetNumber(); err == nil {
			ret = append(ret, n)
		} else if c.Domain[0] == ',' {
			c.Domain = c.Domain[1:]
			continue
		} else if c.Domain[0] == ')' {
			c.Domain = c.Domain[1:]
			return ret, nil
		} else {
			panic("未识别到),遇到未知字符:" + string(c.Domain))
		}
	}
}
func (c *DOMAIN) GetString() (string, error) {
	if c.Domain[0] != '\'' {
		return "", errors.New("第一个字符必须是(" + string(c.Domain))
	}
	c.Domain = c.Domain[1:] //去掉(
	ret := ""
	for {
		if len(c.Domain) == 0 {
			panic("字符串没有结束！")
		} else if c.Domain[0] == '\'' {
			c.Domain = c.Domain[1:]
			return ret, nil
		} else {
			ret += string(c.Domain[0:1])
			c.Domain = c.Domain[1:]
			continue
		}
	}
}
func (c *DOMAIN) GetWord() (string, error) {
	var word = ""
	for {
		by := c.Domain[0]
		if (by >= '0' && by <= '9') || (by >= 'a' && by <= 'z') || (by >= 'A' && by <= 'Z') || by == '.' || by == '-' {
			c.Domain = c.Domain[1:]
			word += string(by)
		} else if by > 0x80 {
			c.Domain = c.Domain[1:]
			word += string(by)
		} else {
			break
		}
	}
	if len(word) == 0 {
		return "", errors.New("字段不能为空,剩余:" + string(c.Domain))
	}
	return word, nil
}

// 从Domain 中获取一个数字
func (c *DOMAIN) GetNumber() (int, error) {
	var strnum string
	for _, v := range c.Domain {
		if v >= '0' && v <= '9' { //数字
			strnum += string(v)
		} else {
			break
		}
	}
	c.Domain = c.Domain[len(strnum):]
	return strconv.Atoi(strnum)
}
func (c *DOMAIN) JX_Interface(src string, srctype int, control string, dec string, dectype int) error {
	minfo, ok := fmTables[strings.ToLower(c.Model)]
	if ok == false {
		panic("未发现模块:" + c.Model)
	}
	var decobj interface{} //目标类型
	decobj = dec           //设置默认值
	dbkey := m_key[c.Zdb.dbtype]
	区分符 := dbkey["字段区分符号"]
	//把源数据和目标数据转换为指定类型的数据
	if srctype == DOMAIN_FIELD {
		fs, err := GetModelField(minfo.Name, src) //获取当前字段的属性
		if err != nil {
			return err
		}
		switch fs.Type {
		case TYPE_INT, TYPE_UINT8, TYPE_UINT16, TYPE_INT32: //目标值
			switch control {
			case "=", "!=", ">", "<", ">=", "<=":
				if dectype == DOMAIN_DEFINE {
					switch dec {
					case "UID":
						decobj = fmt.Sprintf("%d", c.uid.Id)
					default:
						panic("无法识别的宏定义")
					}
				}
				cv, err := ConvInterface2Type(decobj, fs.TypeObj)
				if err != nil {
					return err
				}
				c.Sql = fmt.Sprintf("%s%s%s%s%d", 区分符, fs.Field, 区分符, control, cv)
				return nil
			case "in":
				if dectype == DOMAIN_ARRAY {
					sp := strings.Split(dec, ",") //这里要对数组进行数据确认
					for _, v := range sp {
						_, err := strconv.Atoi(v)
						if err != nil {
							panic("无法转换INT数组:" + dec)
						}
					}
					c.Sql = 区分符 + fs.Field + 区分符 + " in (" + dec + ")"
					return nil
				}
			case "not_in":
				if dectype == DOMAIN_ARRAY {
					sp := strings.Split(dec, ",") //这里要对数组进行数据确认
					for _, v := range sp {
						_, err := strconv.Atoi(v)
						if err != nil {
							panic("无法转换INT数组:" + dec)
						}
					}
					c.Sql = 区分符 + fs.Field + 区分符 + " not in (" + dec + ")"
					return nil
				}
			//一个字段的值包含在另一个表中
			case "child_of":
				if dectype == DOMAIN_FIELD {
					sp := strings.Split(dec, ".")
					revs, err := GetDB("").Query(c.uid, sp[0], "", []string{"Id"}, []string{}, 0, 65536, c.Context)
					if err != nil {
						return err
					}
					if len(revs) != 0 {
						c.Sql = 区分符 + fs.Field + 区分符 + " in ("
						for i, v := range revs {
							if i != 0 {
								c.Sql += ","
							}
							c.Sql += fmt.Sprintf("%d", GetInterfaceId(v))
						}
						c.Sql += ")"
					}
					return nil
				}
			}
		case TYPE_FLOAT64:
			cv, err := ConvInterface2Type(decobj, fs.TypeObj)
			if err != nil {
				return err
			}
			strs := strconv.FormatFloat(cv.(float64), 'f', 3, 64)
			switch control {
			case "=", "!=", ">", "<", ">=", "<=":
				c.Sql = 区分符 + fs.Field + 区分符 + control + strs
				return nil
			}
		case TYPE_BOOL:
			cv, err := ConvInterface2Type(decobj, fs.TypeObj)
			if err != nil {
				return err
			}
			strs := strconv.FormatBool(cv.(bool))
			switch control {
			case "=", "!=":
				c.Sql = 区分符 + fs.Field + 区分符 + control + strs
				return nil
			}
		case TYPE_STRING:
			cv, err := ConvInterface2Type(decobj, fs.TypeObj)
			if err != nil {
				return err
			}
			switch control {
			case "=", "!=":
				c.Sql = 区分符 + fs.Field + 区分符 + control + "'" + cv.(string) + "'"
				return nil
			case "like":
				c.Sql = 区分符 + fs.Field + 区分符 + " LIKE '%" + cv.(string) + "%'"
				return nil
			case "llike":
				c.Sql = 区分符 + fs.Field + 区分符 + " LIKE '" + cv.(string) + "%'"
				return nil
			case "rlike":
				c.Sql = 区分符 + fs.Field + 区分符 + " LIKE '%" + cv.(string) + "'"
				return nil
			}
		case TYPE_MANY2ONE:
			cv, err := ConvInterface2Type(decobj, fs.TypeObj)
			if err != nil {
				return err
			}
			switch control {
			case "=":
				//c.Sql = src + control + "'" + strconv.Itoa(reflect.ValueOf(cv).Elem().FieldByName("Id").Interface().(int)) + "'"
				c.Sql = fmt.Sprintf("`%s`%s%d", fs.Field, control, cv)
				return nil
			}
		case TYPE_MANY2MANY:
			if dectype == DOMAIN_DEFINE {
				switch dec {
				case "UID":
					switch control {
					case "have": // Group have #uid
						c.Sql = 区分符 + fs.Field + 区分符 + " LIKE '%," + fmt.Sprintf("%d", c.uid.Id) + ",%'"
						return nil
					}
				case "GROUP":
					switch control {
					case "have": // Group have #uid
						c.Sql = ""
						for _, group := range c.uid.Groups {
							if len(c.Sql) != 0 {
								c.Sql += " OR "
							}
							c.Sql += "(" + 区分符 + fs.Field + 区分符 + " LIKE '%," + fmt.Sprintf("%d", group.Id) + ",%')"
						}
						c.Sql = "(" + c.Sql + ")"
						return nil
					}
				}

			}
		case TYPE_DATETIME:
			cv, err := ConvInterface2Type(decobj, fs.TypeObj)
			if err != nil {
				return err
			}
			switch control {
			case "=", ">", "<", ">=", "<=", "!=":
				c.Sql = 区分符 + fs.Field + 区分符 + control + "'" + cv.(time.Time).Format("2006-01-02 15:04:05") + "'"
				return nil
			}
		}
		panic("类型标识[" + strconv.Itoa(fs.Type) + "]不支持的操作符:" + control)
	}

	panic("类型标识[" + strconv.Itoa(0) + "]不支持的操作符:" + control)
}
func (c *DOMAIN) JX_numarr(src string, control string, nums []int) error {
	if len(nums) == 0 { //如果是这个，就不用搜索了
		c.Sql += "false"
	}
	c.Sql += src + " in ("
	for i, v := range nums {
		if i != 0 {
			c.Sql += ","
		}
		c.Sql += strconv.Itoa(v)
	}
	c.Sql += ")"
	//c.Args = append(c.Args, nums)
	return nil
}

//功能 解析
func (c *DOMAIN) Jx_jx(src string, control string, table string) error {
	if len(table) == 0 {
		panic("表名不能为空")
	}
	sp := strings.Split(table, ".")
	tabname := sp[0]
	/*tab, err := GetTable(tabname)
	if err != nil {
		return errors.New("未知模块:" + tabname)
	}*/
	var field string
	if len(sp) == 1 { //如果不写，则默认ID
		field = "Id"
	} else {
		field = sp[1]
	}
	switch control {
	case "cin":
		Domain := c.Zdb.GetModelDomain(c.uid, tabname, c.tp)
		if len(Domain) != 0 {
			n := &DOMAIN{Domain: []rune(Domain), tp: c.tp}
			if err := n.Parse(); err != nil {
				return err
			}
			if len(n.Sql) != 0 {
				c.Sql = src + " in (" + "SELECT " + field + " FROM " + tabname + " WHERE " + n.Sql + ")  "
				/*for _, v := range n.Args {
					c.Args = append(c.Args, v)
				}*/
			}
		}

	case "cnotin":
		src = src + " not in (" + "SELECT " + field + " FROM " + tabname + ")"
	default:
		panic(fmt.Sprintf("不支持的操作:src:%s,control:%s,table:%s", src, control, table))
	}
	return nil
}

/*
//功能 快速生成一个Domain格式
func Domain(model interface{}, src string, control string, value interface{}) (string, error) {
	ret := &DOMAIN{}
	if t, ok := model.(string); ok == true {
		ret.Model = t
	} else {
		minfo, err := GetTableInfo(model)
		if err != nil {
			return "", err
		}
		ret.Model = minfo.Name
	}
	ftype, err := GetModelField(ret.Model, src)
	if err != nil {
		panic(err)
	}
	val2, err := ConvInterface2Type(value, ftype)
	if err != nil {
		panic(err)
	}
	switch ftype.Type {
	case TYPE_INT:
	default:
		panic(fmt.Sprintf("未处理： 表:%s 字段%s,比较字符:%s 值类型:%T 值:%v,转换后:%v", ret.Model, src, control, value, value, val2))
	}
	return "", errors.New("未实例化")
}
func (c *DOMAIN) And(src string, control string, value interface{}) (*DOMAIN, error) {
	return nil, errors.New("未实例化")
}
func (c *DOMAIN) Or(src string, control string, value interface{}) (*DOMAIN, error) {
	return nil, errors.New("未实例化")
}
*/
func (c *DOMAIN) GetOneWord() (string, int, error) {
	by := c.Domain[0]
	if by == '#' { //宏定义
		c.Domain = c.Domain[1:]
		if t, err := c.GetWord(); err != nil {
			return "", 0, err
		} else {
			//fmt.Printf("宏:" + t + "\r\n")
			return t, DOMAIN_DEFINE, nil
		}
	} else if by >= 0 && by <= 9 {
		if i, err := c.GetNumber(); err != nil {
			return "", 0, err
		} else {
			return strconv.Itoa(i), DOMAIN_NUM, nil
		}
	} else if by == '\'' {
		c.Domain = c.Domain[1:]
		ret := ""
		for {
			if len(c.Domain) == 0 {
				return "", 0, errors.New("字符串没有结尾")
			}
			if c.Domain[0] == '\'' {
				c.Domain = c.Domain[1:]
				return ret, DOMAIN_STRING, nil
			}
			ret += string(c.Domain[0:1])
			c.Domain = c.Domain[1:]
		}
	} else if by == '(' {
		c.Domain = c.Domain[1:]
		ret := ""
		for {
			if t, err := c.GetWord(); err != nil {
				return "", 0, err
			} else {
				ret += t
			}
			if len(c.Domain) == 0 {
				return "", 0, errors.New("数组没有结尾")
			}
			if c.Domain[0] == ')' {
				c.Domain = c.Domain[1:]
				return ret, DOMAIN_ARRAY, nil
			}
			if c.Domain[0] != ',' {
				return "", 0, errors.New("()未知字符")
			}
			ret += string(c.Domain[0:1])
			c.Domain = c.Domain[1:]
		}
	} else {
		if t, err := c.GetWord(); err != nil {
			return "", 0, err
		} else {
			if t == "true" || t == "false" {
				return t, DOMAIN_BOOL, nil
			}
			return t, DOMAIN_FIELD, nil
		}
	}
}
